package cn.com.zero.common.auth.security.provider;

import cn.com.zero.base.constant.ZeroConstants;
import cn.com.zero.base.exception.ErrorCodeException;
import cn.com.zero.common.auth.security.exception.LoginFailureException;
import cn.com.zero.common.auth.security.service.AbstractLoginAuthenticationService;
import cn.com.zero.common.auth.security.token.MobileCodeAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @author xiongxj
 * @version 1.0.0
 * @Description 手机验证码认证。参考 DaoAuthenticationProvider 继承自定义的 AbstractAuthenticationProvider 进行实现
 * @createTime 2023/3/15 13:47
 */
public class MobileCodeAuthenticationProvider extends AbstractAuthenticationProvider {
    private UserDetailsService userDetailsService;
    private AbstractLoginAuthenticationService loginAuthenticationService;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication) throws AuthenticationException {
        if (authentication.getPrincipal() == null) {
            this.logger.debug("Failed to authenticate since no principal provided");
            throw new BadCredentialsException(this.messages
                    .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad principal"));
        }
        if (authentication.getCredentials() == null) {
            this.logger.debug("Failed to authenticate since no credentials provided");
            throw new LoginFailureException("验证码不允许为空", new ErrorCodeException(-80001L));
        }
        String mobile = (String) authentication.getPrincipal();
        String mobileCode = (String) authentication.getCredentials();

        // 验证验证码是否正确
        Long checkResult = loginAuthenticationService.checkLoginVerificationCode(mobile, mobileCode);
        if (ZeroConstants.RETURN_CODE_SUCCESS.compareTo(checkResult) != 0) {
            throw new LoginFailureException("验证码校验失败", new ErrorCodeException(checkResult));
        }
    }

    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        MobileCodeAuthenticationToken result = new MobileCodeAuthenticationToken(principal, authentication.getCredentials(), user.getAuthorities());
        result.setDetails(authentication.getDetails());
        return result;
    }

    @Override
    protected UserDetails retrieveUser(String username, Authentication authentication) throws AuthenticationException {
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            if (null == userDetails) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return userDetails;
        } catch (UsernameNotFoundException e) {
            throw e;
        } catch (InternalAuthenticationServiceException e) {
            throw e;
        } catch (Exception e) {
            throw new InternalAuthenticationServiceException(e.getMessage(), e);
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return MobileCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setLoginAuthenticationService(AbstractLoginAuthenticationService loginAuthenticationService) {
        this.loginAuthenticationService = loginAuthenticationService;
    }
}
